package com.hjj.homieMatching.manager;

import io.minio.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

@Slf4j
@Component
@Data
@ConfigurationProperties(prefix = "minio")
public class MinIOManager {

    private String endpoint;       // 如: http://虚拟机的ip:19000
    private String accessKey;      // 登录用户名 (root)
    private String secretKey;      // 登录密码 (Aa12345678)
    private String bucketName;     // 存储桶名称 (如: homie)
    private String folderName;     // 存储目录 (如: avatar/)
    
    /**
     * 图片文件扩展名白名单
     */
    private static final List<String> ALLOWED_EXTENSIONS = Arrays.asList(
            "jpg", "jpeg", "png", "gif", "bmp", "webp"
    );
    
    /**
     * 最大文件大小 (5MB)
     */
    private static final long MAX_FILE_SIZE = 5 * 1024 * 1024;

    /**
     * 上传文件到MinIO
     */
    public String upload(MultipartFile file) throws IOException {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("文件不能为空");
        }
        
        // 1. 文件校验
        validateFile(file);
        
        log.info("开始上传文件到MinIO, 文件名: {}, 大小: {}, 配置: endpoint={}, bucket={}", 
                 file.getOriginalFilename(), file.getSize(), endpoint, bucketName);

        // 1.5 先测试连接
        try {
            testConnection();
        } catch (IOException e) {
            log.warn("MinIO连接测试失败: {}", e.getMessage());
            // 这里我们不立即抛出异常，让上传流程继续尝试
        }

        // 2. 构建MinIO客户端
        MinioClient minioClient = null;
        try {
            minioClient = buildMinioClient();
        } catch (Exception e) {
            log.error("MinIO客户端创建失败: {}", e.getMessage(), e);
            throw new IOException("文件存储服务初始化失败: " + e.getMessage());
        }

        try {
            // 3. 确保存储桶存在
            createBucketIfNotExist(minioClient);

            // 4. 生成唯一文件名
            String fileName = generateFileName(file.getOriginalFilename());
            log.info("生成的文件名: {}", fileName);

            // 5. 上传文件
            try {
                minioClient.putObject(
                        PutObjectArgs.builder()
                                .bucket(bucketName)
                                .object(fileName)
                                .stream(file.getInputStream(), file.getSize(), -1)
                                .contentType(file.getContentType())
                                .build()
                );
                
                log.info("文件上传成功: {}", fileName);

                // 6. 返回访问URL（修正 URL 格式）
                // 原格式：String fileUrl = String.format("%s/%s/%s", endpoint, bucketName, fileName);
                // 调整为标准 MinIO URL 格式
                String fileUrl;
                if (endpoint.endsWith("/")) {
                    // 如果 endpoint 已经以 / 结尾，不需要额外加斜杠
                    fileUrl = String.format("%s%s/%s", endpoint, bucketName, fileName);
                } else {
                    // 标准格式: http://minioip:port/bucketName/fileName
                    fileUrl = String.format("%s/%s/%s", endpoint, bucketName, fileName);
                }
                
                // 确保 URL 可以在前端直接访问，替换 localhost 为实际可访问的地址
                if (fileUrl.contains("127.0.0.1") || fileUrl.contains("localhost")) {
                    log.warn("MinIO URL 包含本地地址，可能在前端无法访问: {}", fileUrl);
                    // 尝试使用服务器的公网 IP 或域名
                    // 这里可以根据实际部署环境调整
                }
                
                // 确保 URL 不以 @ 开头
                if (fileUrl.startsWith("@")) {
                    fileUrl = fileUrl.substring(1);
                    log.info("移除URL中的@前缀: {}", fileUrl);
                }
                
                log.info("生成的文件URL: {}", fileUrl);
                return fileUrl;
            } catch (Exception e) {
                log.error("尝试上传文件时出错: {}", e.getMessage(), e);
                // 更详细的错误诊断
                if (e.getCause() instanceof ConnectException || e.getCause() instanceof SocketTimeoutException) {
                    throw new IOException("无法连接到文件存储服务: " + e.getMessage());
                } else if (e.getMessage().contains("credentials")) {
                    throw new IOException("文件存储服务认证失败: " + e.getMessage());
                } else if (e.getMessage().contains("bucket")) {
                    throw new IOException("存储桶操作失败: " + e.getMessage());
                } else {
                    throw new IOException("上传文件失败: " + e.getMessage());
                }
            }
        } catch (IOException e) {
            // 不处理，直接向上抛出
            throw e;
        } catch (Exception e) {
            log.error("文件上传失败: {}", e.getMessage(), e);
            throw new IOException("文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 测试MinIO连接
     * @throws IOException 如果连接不成功
     */
    public void testConnection() throws IOException {
        try {
            log.info("开始测试MinIO连接: {}", endpoint);
            MinioClient minioClient = buildMinioClient();
            minioClient.listBuckets();
            log.info("MinIO连接测试成功");
        } catch (Exception e) {
            String errorMessage;
            if (e.getCause() instanceof ConnectException) {
                errorMessage = "MinIO服务连接被拒绝，服务可能未启动或地址错误: " + endpoint;
            } else if (e.getCause() instanceof SocketTimeoutException) {
                errorMessage = "MinIO服务连接超时: " + endpoint;
            } else if (e.getMessage().contains("InvalidAccessKeyId")) {
                errorMessage = "MinIO访问密钥ID无效";
            } else if (e.getMessage().contains("SignatureDoesNotMatch")) {
                errorMessage = "MinIO密钥签名不匹配";
            } else {
                errorMessage = "MinIO连接测试失败: " + e.getMessage();
            }
            log.error(errorMessage, e);
            throw new IOException(errorMessage);
        }
    }

    /**
     * 创建MinIO客户端
     */
    private MinioClient buildMinioClient() {
        if (endpoint == null || endpoint.trim().isEmpty()) {
            throw new IllegalArgumentException("MinIO端点URL不能为空");
        }
        
        if (accessKey == null || accessKey.trim().isEmpty() || secretKey == null || secretKey.trim().isEmpty()) {
            throw new IllegalArgumentException("MinIO访问凭证不能为空");
        }
        
        try {
            return MinioClient.builder()
                    .endpoint(endpoint)
                    .credentials(accessKey, secretKey)
                    .build();
        } catch (Exception e) {
            log.error("创建MinIO客户端失败: {}", e.getMessage(), e);
            throw new RuntimeException("构建文件存储客户端失败: " + e.getMessage());
        }
    }

    private void validateFile(MultipartFile file) {
        // 文件大小校验
        if (file.getSize() > MAX_FILE_SIZE) {
            throw new IllegalArgumentException("文件大小不能超过5MB");
        }

        // 获取原始文件名
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || originalFilename.isEmpty()) {
            throw new IllegalArgumentException("文件名不能为空");
        }

        // 文件类型校验
        String fileExtension = getFileExtension(originalFilename);
        if (!ALLOWED_EXTENSIONS.contains(fileExtension.toLowerCase())) {
            throw new IllegalArgumentException("仅支持以下图片格式: " + ALLOWED_EXTENSIONS);
        }
        
        log.info("文件验证通过: {}, 扩展名: {}", originalFilename, fileExtension);
    }

    private void createBucketIfNotExist(MinioClient minioClient) throws Exception {
        try {
            boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder()
                    .bucket(bucketName).build());
            
            if (!bucketExists) {
                log.info("存储桶 {} 不存在，正在创建...", bucketName);
                minioClient.makeBucket(MakeBucketArgs.builder()
                        .bucket(bucketName).build());
                log.info("存储桶 {} 创建成功", bucketName);
            } else {
                log.info("存储桶 {} 已存在", bucketName);
            }
        } catch (Exception e) {
            log.error("检查/创建存储桶失败: {}", e.getMessage(), e);
            throw e;
        }
    }

    private String getFileExtension(String filename) {
        if (filename == null || filename.isEmpty()) {
            return "jpg"; // 默认扩展名
        }
        
        // 更加健壮的扩展名获取逻辑
        int lastDotPos = filename.lastIndexOf(".");
        if (lastDotPos < 0 || lastDotPos == filename.length() - 1) {
            // 没有扩展名或扩展名为空
            return "jpg"; // 默认扩展名
        }
        
        // 提取并转换为小写
        String extension = filename.substring(lastDotPos + 1).toLowerCase();
        
        // 验证扩展名是否有效 (额外检查)
        if (!ALLOWED_EXTENSIONS.contains(extension)) {
            log.warn("无效的文件扩展名: {}, 使用默认值: jpg", extension);
            return "jpg";
        }
        
        return extension;
    }

    private String generateFileName(String originalFilename) {
        // 安全处理文件名
        String extension = getFileExtension(originalFilename);
        // 确保返回的URL有正确的扩展名
        log.info("生成文件名，原始名称: {}, 使用扩展名: {}", originalFilename, extension);
        return folderName + UUID.randomUUID().toString() + "." + extension;
    }
}